package cn.thoughtworks.school.controllers;

import cn.thoughtworks.school.annotations.Auth;
import cn.thoughtworks.school.entities.*;
import cn.thoughtworks.school.handlers.BusinessException;
import cn.thoughtworks.school.repositories.BasicQuizChoicesRepository;
import cn.thoughtworks.school.repositories.BasicQuizRepository;
import cn.thoughtworks.school.repositories.BasicQuizSubmissionRepository;
import cn.thoughtworks.school.services.BasicQuizService;
import cn.thoughtworks.school.services.QuizService;
import cn.thoughtworks.school.services.UserCenterService;
import cn.thoughtworks.school.services.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@RequestMapping(value = "/api/v3/basicQuizzes")
@Slf4j
public class BasicQuizController {
  @Autowired
  private BasicQuizRepository basicQuizRepository;
  @Autowired
  private BasicQuizChoicesRepository basicQuizChoicesRepository;
  @Autowired
  private BasicQuizSubmissionRepository basicQuizSubmissionRepository;
  @Autowired
  private Utils utils;
  @Autowired
  private UserCenterService userCenterService;
  @Autowired
  private BasicQuizService basicQuizService;
  @Autowired
  private QuizService quizService;

  @Value("${basic-type}")
  private String type;

  @RequestMapping(value = "", method = RequestMethod.POST)
  @Transactional
  public ResponseEntity saveBasicQuiz(@RequestBody Map data, @Auth User user) {
    Long organizationId = userCenterService.getUserCurrentOrganizationId(user.getId());
    String answer = formatAnswer(data);
    String type = (String) data.get("type");
    BasicQuiz basicQuiz = new BasicQuiz((String) data.get("description"), type, answer);
    basicQuiz.setMakerId(user.getId());
    basicQuiz.setAvailable(true);
    basicQuiz.setOrganizationId(organizationId);
    basicQuiz.setCreateTime(new Date());
    basicQuiz.setTags(utils.formatTags((List<String>) data.get("tags"), user.getId()));
    basicQuiz = basicQuizRepository.save(basicQuiz);

    if (!type.equals("BASIC_BLANK_QUIZ")) {
      List<BasicQuizChoices> choices = basicQuizService.genBasicQuizChoices((List<String>) data.get("choices"), basicQuiz.getId());
      basicQuizChoicesRepository.saveAll(choices);
    }

    Map<String, Object> body = new HashMap<>();
    body.put("uri", "/api/v3/basicQuizzes/" + basicQuiz.getId());
    body.put("id", basicQuiz.getId());
    return new ResponseEntity<>(body, HttpStatus.CREATED);
  }

  private String formatAnswer(Map data) {
    if (data.get("type").equals("MULTIPLE_CHOICE")) {
      List<String> answers = ((List<String>) data.get("answer")).stream()
        .collect(Collectors.toList());
      return answers.stream().reduce("", (p, n) -> p + (p.equals("") ? "" : ",") + n);
    }

    return (String) data.get("answer");
  }

  @RequestMapping(value = "/{id}", method = RequestMethod.GET)
  public ResponseEntity getBasicQuiz(@PathVariable Long id) throws BusinessException {
    BasicQuiz basicQuiz = basicQuizRepository
      .findByIdAndIsAvailableIsTrue(id)
      .orElseThrow(() -> new BusinessException("该题不存在"));

    List<Map> users = userCenterService.getUsersByIds(basicQuiz.getId().toString());

    Map basicMap = basicQuizService.formatBasicQuizByUsers(basicQuiz, users);

    return new ResponseEntity<>(basicMap, HttpStatus.OK);
  }

  @RequestMapping(value = "/students/{studentId}/assignments/{assignmentId}/quizzes/{ids}", method = RequestMethod.GET)
  public ResponseEntity getBasicQuizList(@PathVariable Long studentId, @PathVariable Long assignmentId, @PathVariable String ids) {

    List<Long> idList = Arrays.stream(ids.split(","))
      .map(Long::parseLong)
      .collect(Collectors.toList());
    List<BasicQuiz> basicQuizzes = basicQuizRepository.findAllByIsAvailableIsTrueAndIdIn(idList);


    List<BasicQuizSubmission> basicQuizSubmissions = basicQuizSubmissionRepository.findByQuizIdInAndAssignmentIdAndUserIdOrderByIdDesc(idList, assignmentId, studentId);

    List<Map> results = basicQuizzes.stream()
      .map(basicQuiz -> utils.addUserAnswerWithBasicQuiz(basicQuiz, basicQuizSubmissions))
      .collect(Collectors.toList());

    return new ResponseEntity<>(results, HttpStatus.OK);

  }

  @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
  @Transactional
  public ResponseEntity updateBasicQuiz(@PathVariable Long id, @RequestBody Map updateQuiz, @Auth User user) throws BusinessException {
    BasicQuiz basicQuiz = basicQuizRepository
      .findByIdAndIsAvailableIsTrue(id)
      .orElseThrow(() -> new BusinessException(
        String.format("Unknown basicQuiz with id: %s", id)
      ));

    String type = basicQuiz.getType();
    updateQuiz.put("type", type);
    String answer = formatAnswer(updateQuiz);

    basicQuiz.setAnswer(answer);
    basicQuiz.setUpdateTime(new Date());
    basicQuiz.setDescription((String) updateQuiz.get("description"));
    basicQuiz.setMakerId(user.getId());
    basicQuiz.setTags(utils.formatTags((List<String>) updateQuiz.get("tags"), user.getId()));

    if (!type.equals("BASIC_BLANK_QUIZ")) {
      List<String> options = (List<String>) updateQuiz.get("choices");
      List<BasicQuizChoices> choices = basicQuiz.getChoices();
      int i = 0;
      for (String option : options) {
        choices.get(i).setChoice(option);
        i++;
      }

      basicQuiz.setChoices(choices);
    }
    basicQuizRepository.save(basicQuiz);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  @GetMapping(value = "pageable")
  public ResponseEntity getBasicQuizzesPageable(@PageableDefault(sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable,
                                                     @RequestParam(required = false)  String quizType,
                                                     @RequestParam(required = false) Long quizId,
                                                     @RequestParam(required = false) Long userId,
                                                     @RequestParam(required = false) String description,
                                                     @RequestParam(required = false) List<String> tags,
                                                     @Auth User user) {
    Page basicQuizzesPage = basicQuizService.getBasicQuizzesPageable(quizType, pageable,quizId,userId,description,tags,user);
    return new ResponseEntity<>(basicQuizzesPage, HttpStatus.OK);
  }

  @RequestMapping(value = "/students/{studentId}/assignments/{assignmentId}/quizzes", method = RequestMethod.POST)
  public ResponseEntity submitBasicQuiz(@PathVariable Long studentId, @PathVariable Long assignmentId, @RequestBody List<Map> data) throws BusinessException {
    List<Boolean> quizSubmissionList = data.stream().map(item -> {
      Long quizId = Long.valueOf((int) item.get("quizId"));
      BasicQuiz basicQuiz = basicQuizRepository
        .findByIdAndIsAvailableIsTrue(quizId)
        .orElse(new BasicQuiz());

      String userAnswer = (String) item.get("userAnswer");
      BasicQuizSubmission basicQuizSubmission = new BasicQuizSubmission(assignmentId, (long) quizId, studentId, userAnswer);
      basicQuizSubmission.setCorrect(basicQuizService.isAnswerCorrect(basicQuiz, userAnswer));
      basicQuizSubmission.setSubmitTime(new SimpleDateFormat("YYYY-MM-dd HH-mm-ss").format(new Date()));
      basicQuizSubmissionRepository.save(basicQuizSubmission);
      return basicQuizSubmission.getCorrect();
    }).collect(Collectors.toList());
    return new ResponseEntity<>(quizSubmissionList, HttpStatus.OK);
  }

  @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
  public ResponseEntity deleteBasicQuiz(@PathVariable Long id) {

    BasicQuiz basicQuiz = basicQuizRepository
      .findByIdAndIsAvailableIsTrue(id)
      .orElse(null);

    if (basicQuiz != null) {
      basicQuiz.setAvailable(false);
      basicQuizRepository.save(basicQuiz);
    }

    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  @RequestMapping(value = "/selecting/{ids}", method = RequestMethod.GET)
  public ResponseEntity getBasicQuizzes(@PathVariable String ids) {
    List<Long> quizIds = Stream.of(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
    List<BasicQuiz> basicQuizzes = basicQuizRepository.findAllByIsAvailableIsTrueAndIdIn(quizIds);

    String userIdStr = utils.getMakerIds(basicQuizzes);

    List<Map> users = userCenterService.getUsersByIds(userIdStr);

    List quizzes = basicQuizzes.stream()
      .map(basicQuiz -> basicQuizService.formatBasicQuizByUsers(basicQuiz, users))
      .collect(Collectors.toList());

    return new ResponseEntity<>(quizzes, HttpStatus.OK);
  }

  @PostMapping("excel")
  public ResponseEntity excelImport(@RequestParam("file") MultipartFile file,
                                    @Auth User current) throws BusinessException, IOException {
    if (validateExcelFormat(file)) {
      basicQuizService.excelImport(file, current);
      return new ResponseEntity(HttpStatus.CREATED);
    }
    throw new BusinessException("Wrong format. Only support .xls or .xlsx");
  }

  @PostMapping("yml")
  public ResponseEntity ymlImport(@RequestParam("file") MultipartFile file,
                                    @Auth User current) throws BusinessException, IOException {
    if (validateYmlZipFormat(file)) {
      quizService.ymlImport(file, current);
      return new ResponseEntity(HttpStatus.CREATED);
    }
    throw new BusinessException("Wrong format");
  }

  private boolean validateYmlZipFormat(MultipartFile file) {
    String fileName = file.getOriginalFilename();
    return fileName.endsWith(".zip");
  }

  public Boolean validateExcelFormat(MultipartFile file) {
    String fileName = file.getOriginalFilename();
    return fileName.endsWith(".xls") || fileName.endsWith(".xlsx");
  }

    @GetMapping("assignments/{assignmentId}/score")
    public ResponseEntity calculateBasicQuizAssignmentScore(@PathVariable Long assignmentId) {
        return new ResponseEntity(basicQuizService.calculateAssignmentScore(assignmentId), HttpStatus.OK);
    }
}


